package com.softserveinc.ita.kaiji.multiplay.domain.table;

import com.softserveinc.ita.kaiji.multiplay.domain.GameElementBase;
import com.softserveinc.ita.kaiji.multiplay.domain.InvalidActionException;
import com.softserveinc.ita.kaiji.multiplay.domain.card.Card;
import com.softserveinc.ita.kaiji.multiplay.domain.message.InOutMessage;
import com.softserveinc.ita.kaiji.multiplay.domain.message.InvalidMessageException;
import com.softserveinc.ita.kaiji.multiplay.domain.message.MessageProcessable;
import com.softserveinc.ita.kaiji.multiplay.domain.player.ShipPlayer;
import com.softserveinc.ita.kaiji.multiplay.domain.player.StateOfPlayer;
import com.softserveinc.ita.kaiji.multiplay.domain.room.Ship;
import com.softserveinc.ita.kaiji.multiplay.dto.server.table.DtoTable;

/**
 * Project kaiji-game
 * Created by mikeldpl
 * 29.03.2015 0:39.
 */
public class ShipTable extends GameElementBase implements MessageProcessable {

	public static final long TIME_OUT_AFTER_DUEL = 3000;

	private final int id;
	private final Side sidesL = new Side();
	private final Side sidesR = new Side();
	private final Ship ship;
	private StateOfTable state = StateOfTable.EMPTY;

	public ShipTable(int id, Ship ship) {
		this.id = id;
		this.ship = ship;
	}

	public boolean isFree() {
		return state == StateOfTable.EMPTY;
	}

	//-------------------------------------------------core

	public void sitOn(InOutMessage msg, ShipPlayer left, ShipPlayer right) {
		if (isFree()) {
			sidesR.setPlayer(msg, right);//first is second
			sidesL.setPlayer(msg, left);
			setState(msg, StateOfTable.IN_GAME);
			msg.addDomainThatMustBeSanded(this);
		} else {
			throw new InvalidActionException(this + " can't sit " + left + " " + right);
		}
	}

	public void sitOut(InOutMessage msg) {
		sidesL.reset(msg);
		sidesR.reset(msg);
		setState(msg, StateOfTable.EMPTY);
	}

	public void sitOutWithDelay() {
		new Thread(new Runnable() {
			@Override
			public void run() {
				synchronized (ShipTable.this.getSidesL().getPlayer().getPublisher()) {
					InOutMessage msg = new InOutMessage(null, null);
					msg.addDomainThatMustBeSanded(sidesL.getPlayer().getPublisher());
					if (sidesR.getPlayer().getState() == StateOfPlayer.IN_DUEL) {
						sidesR.getPlayer().setState(msg, StateOfPlayer.ONLINE);
					}
					if (sidesL.getPlayer().getState() == StateOfPlayer.IN_DUEL) {
						sidesL.getPlayer().setState(msg, StateOfPlayer.ONLINE);
					}
					try {
						Thread.sleep(TIME_OUT_AFTER_DUEL);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					sitOut(msg);
					msg.sendResponse();
				}
			}
		}).start();
	}

	@Override
	public void processMessage(InOutMessage message) {

		Card card = new Card(message.getIn().getAction(), ship.getDeck().getNumberOfCardsTypesConfig());
		if (sidesL.getPlayer().equals(message.getPlayer()) && sidesL.getCard() == null
				&& sidesL.getPlayer().getDeck().getNumberOfCards(card) > 0) {
			sidesL.setCard(card);
		} else if (sidesR.getPlayer().equals(message.getPlayer()) && sidesR.getCard() == null
				&& sidesR.getPlayer().getDeck().getNumberOfCards(card) > 0) {
			sidesR.setCard(card);
		} else {
			throw new InvalidMessageException("Table " + getId() + " id or player is invalid. Can't find side");
		}
		match(message);
		message.addDomainThatMustBeSanded(this);
	}

	public void match(InOutMessage msg) {
		if (sidesL.getCard() != null && sidesR.getCard() != null) {
			sidesL.validateCardsRate();
			sidesR.validateCardsRate();
			sidesR.getPlayer().removeChallengedPlayer(msg, sidesL.getPlayer());
			sidesL.getPlayer().decreaseCard(msg, sidesL.getCard());
			sidesR.getPlayer().decreaseCard(msg, sidesR.getCard());
			switch (sidesL.getCard().match(sidesR.getCard())) {
				case WIN:
					sidesR.getRate().win(msg, sidesL.getPlayer());
					sidesR.getRate().lost(msg, sidesR.getPlayer());
					break;
				case LOSE:
					sidesL.getRate().win(msg, sidesR.getPlayer());
					sidesL.getRate().lost(msg, sidesL.getPlayer());
					break;
			}
			sitOutWithDelay();
		}
	}

	//---------------------------------------------------------------------------------------
	@Override
	public DtoTable getDto() {
		return new DtoTable(getId());
	}

	@Override
	public DtoTable getEntireDto() {
		DtoTable dto = getDto();
		dto.setLeftName(getSidesL().getPlayer() == null ? null : getSidesL().getPlayer().getName());
		dto.setRightName(getSidesR().getPlayer() == null ? null : getSidesR().getPlayer().getName());
		dto.setState(getState());
		setCardsToDto(dto);
		return dto;
	}

	private DtoTable setCardsToDto(DtoTable dto) {
		if (getSidesL().getCard() == null && getSidesR().getCard() != null) {
			dto.setRightCard(Card.back);
		} else if (getSidesL().getCard() != null) {
			if (getSidesR().getCard() == null) {
				dto.setLeftCard(Card.back);
			} else {
				dto.setLeftCard(getSidesL().getCard().getName());
				dto.setRightCard(getSidesR().getCard().getName());
			}
		}
		return dto;
	}

	//-----------------------------------------------------setters

	public void setState(InOutMessage msg, StateOfTable state) {
		this.state = state;
		msg.addDomainThatMustBeSanded(this);
	}


	//-----------------------------------------------getters


	public StateOfTable getState() {
		return state;
	}

	public int getId() {
		return id;
	}

	public Side getSidesL() {
		return sidesL;
	}

	public Side getSidesR() {
		return sidesR;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;

		ShipTable shipTable = (ShipTable) o;

		return id == shipTable.id;

	}

	@Override
	public int hashCode() {
		return id;
	}
}
